package upm.pb4agent.team5.custom;

import jade.content.lang.Codec.CodecException;
import jade.content.onto.OntologyException;
import jade.core.AID;
import jade.domain.FIPAException;
import jade.lang.acl.ACLMessage;
import upm.pb4agent.ontology.AssignBoat;
import upm.pb4agent.ontology.PickSeat;
import upm.pb4agent.ontology.Row;
import upm.pb4agent.ontology.Seat;
import upm.pb4agent.team5.common.Boat;
import upm.pb4agent.team5.common.Watcher;
import upm.pb4agent.team5.enumeration.ConfigProperty;
import upm.pb4agent.team5.enumeration.RowDirection;
import upm.pb4agent.team5.enumeration.RowStrength;
import upm.pb4agent.team5.util.ConfigFile;
import upm.pb4agent.team5.util.RowerWrapper;

class RowerRowStrategy implements Runnable {
	private static final long WAIT_PER_ROW = 100; // milliseconds
	
	private AID watcher = null;
	private Rower rower = null;

	private boolean changedStrategy = false;
	private int actualOar; // Oars for start rowing preset
	private String bestSeatToRow;
	
	public RowerRowStrategy(Rower rower) {
		this.rower = rower;
		
		try {
			this.watcher = rower.findAID(Watcher.DF_NAME + Manager.TEAM_ID);
			this.bestSeatToRow = calculateBestSeatToRow();
		} catch (FIPAException e) {
			System.out.println(rower.getLocalName() + ": Error locating watcher " + e.getMessage());
		} 
	}
	
	private synchronized void modifyRowingStrategy() throws CodecException, OntologyException, InterruptedException, FIPAException {
		setChangedStrategy(false); // restoring changedStrategy for entering the loop
		
		if (watcher == null)
			watcher = rower.findAID(Watcher.DF_NAME + Manager.TEAM_ID);

		while(!isChangedStrategy()) { 
			row();
			relocateRowers();
			
			Thread.sleep(WAIT_PER_ROW);
		}
	}
	
	private void relocateRowers() throws CodecException, OntologyException, FIPAException {
		int numOfDocks = rower.getDocks().size();
		AID bestRowerAID = rower.getAID();
		float ki = 0;

		if (numOfDocks > 1) {
			for (int i=0; i<numOfDocks; i++) { // Only considering as much rowers as are in the boat
				RowerWrapper rowerWrapper = rower.getRowersWrapper().get(i);
				
				if (ki <= rowerWrapper.getKi()) { // Kis are updated in BRowerManageMessages->RealRow
					ki = rowerWrapper.getKi();
					bestRowerAID = rowerWrapper.getRowerAID();					
				}
			}

			if (!bestRowerAID.equals(rower.getAID()) && rower.isRowing()) { // Change Only if you're Rowing!!!!
				rower.send(rower.getKi(), bestRowerAID, ACLMessage.INFORM, Rower.SEND_KI_PROTOCOL);
			} 
		}
	}
	
	private void row() throws CodecException, OntologyException, FIPAException {
		if (rower.isRowing()) 
			row(getActualOar(), RowDirection.FORWARD, RowStrength.STRONG);
	}
	

	private String getActualOar() {
		if (bestSeatToRow.equals(Boat.SEAT_ID[1])) {
			if (actualOar == 0)
				actualOar =  1;
			else
				actualOar = 0;
		} else {
			if (actualOar == 2)
				actualOar = 3;
			else 
				actualOar = 2;
		}
		
		return Boat.OAR_ID[actualOar];
	}
	
	private void row(String oarId, RowDirection rowDirection, RowStrength rowStrength) throws CodecException, OntologyException, FIPAException {
		Row row = new Row();
		row.setBoat(rower.getBoat());
		row.setDirection(rowDirection.ordinal());
		row.setStrength(rowStrength.ordinal());
		row.setOarID(oarId);
		
		rower.send(row, watcher, ACLMessage.REQUEST);
		System.out.println(rower.getLocalName() + getRowInfo(row));			
	}
	
	public void assignBoat(AssignBoat assignBoat) throws CodecException, OntologyException {
		rower.setBoat(assignBoat.getBoat());
		
		// Picking a seat (only at the beginning, after this, seats should be assigned when new rowers enter the boat)
		RowerWrapper actualRower = rower.getRowersWrapper().get(rower.getDocks().size() - 1);
		if (actualRower.getRowerAID().getLocalName().equals(rower.getDfName())) {
			requestPickSeat(bestSeatToRow);
		}		
	}

	private String calculateBestSeatToRow() {
		float frontOarCoef = Float.parseFloat( 
				ConfigFile.getProperty(ConfigProperty.front_oar_y_coef.name())
			);
		float backOarCoef = Float.parseFloat( 
				ConfigFile.getProperty(ConfigProperty.back_oar_y_coef.name())
			);
		
		if (frontOarCoef > backOarCoef)
			return Boat.SEAT_ID[1];
		else
			return Boat.SEAT_ID[4];
	}
	
	public void requestPickSeat(String seatId) throws CodecException, OntologyException {
		Seat seat = new Seat();
		seat.setSeatID(seatId);
		
		PickSeat pickSeat = new PickSeat();
		pickSeat.setSeatID(seat);
		rower.setSeat(seat.getSeatID()); // Saving seatId for rower
		
		rower.send(pickSeat, rower.getBoat().getID(), ACLMessage.INFORM);
		System.out.println(rower.getLocalName() + ": Request Pick Seat " + seat.getSeatID() + " sent");
	}
		
	private String getRowInfo(Row row) {
		return ": Rowing [" +
			"BOAT: " + row.getBoat().getID().getLocalName() + ", " + 
			"OAR: " + row.getOarID() + ", " +
			"DIRECTION: " + row.getDirection() + ", " +
			"STRENGTH: " + row.getStrength() + "]";
	}
		
	public boolean isChangedStrategy() {
		return changedStrategy;
	}

	public void setChangedStrategy(boolean changedStrategy) {
		this.changedStrategy = changedStrategy;
	}	
	
	public String getBestSeatToRow() {
		return bestSeatToRow;
	}

	@Override
	public void run() {
		try {
			modifyRowingStrategy();
		} catch (Exception e) {
			e.printStackTrace();
			System.err.println(rower.getLocalName() + ": Error modifying strategy");
		}
	}
}